package game;

import java.awt.Point;
import java.util.ArrayList;

/**
 *	@(#)Hexfield.java
 *
 *	<p>The <code>Hexfield</code> class represents one hexagon of a <code>Landtile</code>.</p>
 *	<p>A landtile hexagon has an absolute position within the game provided by its
 *	corresponding <code>Landtile</code> object. It's <code>LandType</code> specifies
 *	what kind of resources you can expect to get from that hexfield. If an exploration chit
 *	is placed on a <code>Hexfield</code>, it will become explorable. After a city or inn was
 *	built on it, it will be marked disabled and will no longer be accessible.</p>
 *	<p>Also, this class calculates the outline of a <code>Hexfield</code>, needed
 *	to highlight it later in the game (zone of control). For this reason, it is connected
 *	to all its neighbouring <code>Hexfield</code>s.</p>
 *
 *	@author Grzegorz Kobiela
 *	@version 1.0 2008/4/23
 */
public final class Hexfield
extends GameObject
{
	/** Index of the neighbour north east of the hexfield. */
	public final static int NORTH_EAST = 0;

	/** Index of the neighbour north of the hexfield. */
	public final static int NORTH = 1;

	/** Index of the neighbour north west of the hexfield. */
	public final static int NORTH_WEST = 2;

	/** Index of the neighbour south east of the hexfield. */
	public final static int SOUTH_EAST = 3;

	/** Index of the neighbour south of the hexfield. */
	public final static int SOUTH = 4;

	/** Index of the neighbour south west of the hexfield. */
	public final static int SOUTH_WEST = 5;

	/** Constant representing a polluted hexfield. */
	public static final int POLLUTED = 0;

	/** Constant representing a polluted hexfield. */
	public static final int UNPOLLUTED = 1;

	/** Constant representing a disabled hexfield. */
	public static final int DISABLED = 2;

	/** All neighbouring hexfields. */
	private Hexfield[] neighbours = new Hexfield[6];

	/** The location of the hexfield on the game board. */
	private Point location;

	/** The landtype of the hexfield. */
	private LandType landType;

	/** The exploration chit lying on the hexfield. */
	private ExplorationChit explorationChit = null;

	/** True if the hexfield is in use. */
	private boolean used = false;

	/** True if the hexfield is polluted. */
	private boolean polluted = false;

	/** True if the hexfield is no longer in play. */
	private boolean disabled = false;

	/**
	 *	Creates a new <code>Hexfield</code> instance with the given land type
	 *	and the coordinates of the location on the game board.
	 *	@param type the land type
	 *	@param x the x-coordinate of the location
	 *	@param y the y-coordinate of the location
	 */
	public Hexfield(LandType type, Point p) {
		landType = type;
		location = new Point(p);
	}

	/**
	 *	Disables the hexfield and makes it no more accessible.
	 *	If there is an exploration chit on it, it will be removed.
	 */
	public void disable() {
		setPolluted(false);
		setUsed(true);
		deactivate();
		disabled = true;
		update(DISABLED);
	}

	/**
	 *	Returns true if the hexfield is disabled.
	 *	@return true if the hexfield is disabled
	 */
	public boolean isDisabled() {
		return disabled;
	}

	/**
	 *	Provide true if this hexfield is in use.
	 *	@param polluted provide true if this hexfield is in use
	 */
	public void setUsed(boolean used) {
		this.used = used;
		if(used) setExplorationChit(null);
	}

	/**
	 *	Returns true if this hexfield is in use.
	 *	@return true if this hexfield is in use
	 */
	public boolean isUsed() {
		return used;
	}

	/**
	 *	Sets the pollution of this hexfield to the given value.
	 *	@param polluted provide true if this hexfield is polluted
	 */
	public void setPolluted(boolean polluted) {
		this.polluted = polluted;
		if(polluted) setExplorationChit(null);
		update(polluted ? POLLUTED : UNPOLLUTED);
	}

	/**
	 *	Returns true if this hexfield is polluted.
	 *	@return true if this hexfield is polluted
	 */
	public boolean isPolluted() {
		return polluted;
	}

	/**
	 *	Returns the land type of the hexfield.
	 *	@return the land type
	 */
	public LandType getType() {
		return landType;
	}

	/**
	 *	Sets the land type of the hexfield.
	 *	@param type the land type
	 */
	public void setType(LandType type) {
		update(landType = type);
	}

	/**
	 *	Returns whether the land type of the hexfield is forest or not.
	 *	@return true if the land type is forest
	 */
	public boolean isForest() {
		return landType.equals(LandType.FOREST);
	}

	/**
	 *	Returns whether the land type of the hexfield is grass or not.
	 *	@return true if the land type is grass
	 */
	public boolean isGrass() {
		return landType.equals(LandType.GRASS);
	}

	/**
	 *	Returns whether the land type of the hexfield is hill or not.
	 *	@return true if the land type is hill
	 */
	public boolean isHill() {
		return landType.equals(LandType.HILL);
	}

	/**
	 *	Returns whether the land type of the hexfield is water or not.
	 *	@return true if the land type is water
	 */
	public boolean isWater() {
		return landType.equals(LandType.WATER);
	}

	/**
	 *	Sets the exploration chit lying on the hexfield.
	 *	@param chit the exploration chit
	 */
	public void setExplorationChit(ExplorationChit chit) {
		if(explorationChit != null) explorationChit.update(null);
		explorationChit = chit;
	}

	/**
	 *	Returns the exploration chit lying on the hexfield.
	 *	@return the exploration chit
	 */
	public ExplorationChit getExplorationChit() {
		return explorationChit;
	}

	/**
	 *	Returns the location of the hexfield on the game board.
	 *	@return the location
	 */
	public Point getLocation() {
		return new Point(location);
	}

	/**
	 *	Sets a specific neighbour of this <code>Hexfield</code>
	 *	(characterized by an integer given in this class' static fields).
	 *	@param i location of the neighbour
	 *	@param hex neighbour at index i
	 */
	public void setNeighbour(int i, Hexfield hex) {
		neighbours[i] = hex;
	}

	/**
	 *	Returns a specific neighbour of this <code>Hexfield</code>
	 *	(characterized by an integer given in this class' static fields).
	 *	@param i location index
	 *	@return neighbour at index i
	 */
	public Hexfield getNeighbour(int i) {
		return neighbours[i];
	}

	/**
	 *	Return all the neighbours of this <code>Hexfield</code>
	 *	including this one if wished.
	 *	@param include provide true to include this hexfield
	 *	@return all neighbours
	 */
	public ArrayList<Hexfield> getNeighbours(boolean include) {
		ArrayList<Hexfield> hexes = new ArrayList<Hexfield>();
		if(include) hexes.add(this);
		for(Hexfield hex: neighbours) hexes.add(hex);
		return hexes;
	}

	/**
	 *	Return all the neighbours of this <code>Hexfield</code>.
	 *	@return all neighbours
	 */
	public ArrayList<Hexfield> getNeighbours() {
		return getNeighbours(false);
	}

	/**
	 *	Checks if there is a neighbour at a specific location (given by
	 *	an integer equal to one of this class' static fields).
	 *	@param i location index
	 *	@return true if there is a neighbour at index i
	 */
	public boolean hasNeighbour(int i) {
		return (neighbours[i] != null);
	}

	/**
	 *	Returns the location of the hexfield at the given position
	 *	relative to the given location.
	 *	@param location the location
	 *	@param position the position
	 *	@return the location of the hexfield
	 */
	public static Point getNeighbour(Point location, int position) {
		switch(position) {
			case NORTH_WEST:
				return new Point(location.x - LandSystem.DX, location.y - LandSystem.DY);

			case NORTH:
				return new Point(location.x, location.y - 2 * LandSystem.DY);

			case NORTH_EAST:
				return new Point(location.x + LandSystem.DX, location.y - LandSystem.DY);

			case SOUTH_WEST:
				return new Point(location.x - LandSystem.DX, location.y + LandSystem.DY);

			case SOUTH:
				return new Point(location.x, location.y + 2 * LandSystem.DY);

			case SOUTH_EAST:
				return new Point(location.x + LandSystem.DX, location.y + LandSystem.DY);

			default:
				return new Point(location);
		}
	}

	/**
	 *	Returns the string representation of this object.
	 *	@return the string representation
	 */
	@Override
	public String toString() {
		return super.toString() + " [" + location.x + "," + location.y + "]";
	}
}